import {
    _decorator,
    Camera,
    color,
    Component,
    EventTarget,
    EventTouch,
    Graphics,
    instantiate,
    Label,
    Node,
    Prefab,
    Size,
    Sprite,
    SpriteFrame,
    SystemEvent,
    systemEvent,
    tween,
    UITransform,
    v2,
    v3,
    Vec2,
    Vec3,
    View
} from 'cc';
import GameData from "./GameData";
import Platform, {PlatformType} from "./Platform";
import {AudioClipID, AudioManager} from "./AudioManager";
import AgreementUI from "./AgreementUI";
import Util from "./Util";

import {
    BallColorIdx,
    ColorTable,
    GameConfig,
    GameMode,
    ItemPrice,
    Items,
    NativeEvt,
    wxCityDBName,
    wxRankDBName
} from "./GameConfig";
import {Grid} from "./Grid";
import {GameOper} from "./GameOper";
import {MainUI} from "./MainUI";
import {BrickParticleManager} from "./BrickParticleManager";
import {StartGameUI} from "./StartGameUI";
import {SettingUI} from "./SettingUI";
import {Guide} from "./Guide";

import {GetMoneyUI} from "./GetMoneyUI";
import {ConfirmDlgUI} from "./ConfirmDlgUI";
import {WXShareUI} from "./WXShareUI";
import {RankUI} from "./RankUI";
import {MoneyAnim} from "./MoneyAnim";
import {DailyAwardUI} from "./DailyAwardUI";
import DailyAwardData from "./DailyAwardData";
import WXUser from "./WXUser";
import {TTShareUI} from "./TTShareUI";
import {LoadingMask} from "./LoadingMask";
import LevelCfgItem from "./LevelCfgItem";
import {Ball} from "./Ball";
import {LevelConditionBall} from "./LevelConditionBall";
import {Rocket} from "./Rocket";
import {GameOverUI} from "./GameOverUI";
import {RetryUI} from "./RetryUI";
import {ResultUI} from "./ResultUI";
import {LineGame} from "./LineGame/LineGame";
import {LineGameHelpUI} from "./LineGameHelpUI";
import {HpNotEnoughUI} from "./HpNotEnoughUI";


const { ccclass, property } = _decorator;
 
@ccclass('GameMain')
export class GameMain extends Component {

    @property({type:Camera})
    rttCamera:Camera=null;

    //r,g,b,o,p,c
    @property({type:[SpriteFrame]})
    ballSpriteFrames:Array<SpriteFrame>=[];

    @property({type:Prefab})
    ballPrefab:Prefab=null;

    @property({type:Prefab})
    gridPrefab:Prefab=null;

    @property({type:Prefab})
    ringPrefab:Prefab=null;

    @property({type:Prefab})
    brickParticlePrefab:Prefab=null;

    @property({type:Prefab})
    ballExplodePrefab:Prefab=null;

    @property({type:Prefab})
    levelPassExplodePrefab:Prefab=null;

    @property({type:Prefab})
    starExplodePrefab:Prefab=null;

    @property({type:Prefab})
    rocketPrefab:Prefab=null;

    @property({type:Node})
    ballParent:Node=null;

    @property({type:Node})
    lineGameNode:Node=null;

    @property({type:Graphics})
    graphics:Graphics=null;

    @property({type:Guide})
    guide:Guide=null;

    @property({type:StartGameUI})
    startGameUI:StartGameUI=null;

    @property({type:MainUI})
    mainUI:MainUI=null;

    @property({type:SettingUI})
    settingUI:SettingUI=null;

    @property({type:ResultUI})
    resultUI:ResultUI=null;

    @property({type:GetMoneyUI})
    getMoneyUI:GetMoneyUI=null;

    @property({type:WXShareUI})
    wxShareUI:WXShareUI=null;

    @property({type:ConfirmDlgUI})
    confirmDlgUI:ConfirmDlgUI=null;

    @property({type:DailyAwardUI})
    dailyAwardUI:DailyAwardUI=null;

    @property({type:GameOverUI})
    gameOverUI:GameOverUI=null;

    @property({type:RetryUI})
    retryUI:RetryUI=null;

    @property({type:RankUI})
    rankUI:RankUI=null;

    @property({type:TTShareUI})
    ttShareUI:TTShareUI=null;

    @property({type:LineGameHelpUI})
    lineGameHelpUI:LineGameHelpUI=null;

    @property({type:HpNotEnoughUI})
    hpNotEnoughUI:HpNotEnoughUI=null;

    @property({type:MoneyAnim})
    moneyAnim:MoneyAnim=null;

    @property({type:Node})
    agreementUINode:Node=null;

    public lineGame:LineGame=null;

    public canvasNode:Node=null;
    public canvasBgSprite:Sprite=null;
    public agreementUI:AgreementUI=null;
    public loadingMask:LoadingMask=null;

    public comboNumLabel:Label=null;
    public comboTextLabel:Label=null;

    public ballAttachNode:Node=null;
    public ballParentUITransform:UITransform=null;
    public gridAttachNode:Node=null;
    public effectAttachNode:Node=null;
    public rocketAttachNode:Node=null;

    public levelPassEffectParent:Node=null;

    public touchTransInput:Vec3=v3(0,0,0);
    public touchTransOutput:Vec3=v3(0,0,0);

    public lastTouchMoveX:number=-9999;
    public lastTouchMoveY:number=-9999;
    public lastTouchGridIdx:number=-1;

    //游戏格子数据
    public grids:Array<Grid>=[];
    public curLevelPassed:boolean=false;

    //关卡配置
    public static levelCfg:Array<LevelCfgItem>=[];

    public gameTimeTick:number=0;
    public playTimes:number=0;
    public gameData:GameData=new GameData();
    public dailyAwardData:DailyAwardData=new DailyAwardData();

    public sTime:boolean=false;

    public gameOper:GameOper=new GameOper();
    public levelMode:GameMode=GameMode.BallGame;

    public curSelItem:Items=Items.Unknow;

    public eventTarget:EventTarget=new EventTarget();

    //屏幕尺寸
    public canvasSize:Size=null;
    public visibleSize:Size=null;
    public designXRatio:number=1;
    public designYRatio:number=1;

    public recordStartTime:number=0;
    public recordTime:number=0;
    public isStartRecord:boolean=false;

    public wxHideTime:number=0;
    public wxShareShowed:boolean=false;
    public wxUser:WXUser=new WXUser();

    public wxRankReqLastTime:number=0;
    public wxRankCityReqLastTime:number=0;
    public wxRankReqTimeInterval:number=5 * 60 * 1000;

    public topCity:string="";

    public vedioAwardScene:string="";

    public playCnt:number=0;

    public static instance=null;

    public static readonly version:string="1.0.5";

    public static readonly HPResumeTimeInterval:number=60 * 60 *1000;

    onLoad()
    {
        GameMain.instance=this;

        this.lineGame=this.lineGameNode.getComponent("LineGame");

        this.canvasNode=this.node.parent.getChildByName("Canvas");
        this.canvasBgSprite=this.canvasNode.getChildByName("bg").getComponent(Sprite);
        this.agreementUI=this.agreementUINode.getComponent("AgreementUI");
        this.loadingMask=this.canvasNode.getChildByName("loadingMask").getComponent("LoadingMask");

        this.ballAttachNode=this.ballParent.getChildByName("ballsAttach");
        this.ballParentUITransform=this.ballAttachNode.getComponent(UITransform);
        this.gridAttachNode=this.ballParent.getChildByName("gridsAttach");
        this.effectAttachNode=this.ballParent.getChildByName("effectAttach");
        this.rocketAttachNode=this.ballParent.getChildByName("rocketAttach");

        this.levelPassEffectParent=this.canvasNode.getChildByName("topEffect").getChildByName("levelPassEffectParent");

        this.startGameUI.node.active=true;

        this.comboNumLabel=this.ballParent.getChildByName("comboNum").getComponent(Label);
        this.comboTextLabel=this.ballParent.getChildByName("comboText").getComponent(Label);

        this.canvasSize=View.instance.getCanvasSize();
        console.log("** canvasSize="+JSON.stringify(this.canvasSize));

        this.visibleSize=View.instance.getVisibleSize();
        console.log("** visibleSize="+JSON.stringify(this.visibleSize));

        this.designXRatio=this.visibleSize.width / this.canvasSize.width;
        this.designYRatio=this.visibleSize.height / this.canvasSize.height;

        this.gameData.load();
        this.dailyAwardData.load();

        console.log("** loadCity="+this.gameData.city);

        //this.gameData.curLevelId=99;
        //console.log("** 当前关卡id="+this.gameData.curLevelId+ "cnt="+GameMain.levelCfg.length);

        this.initEvtTarget();

        if(Platform.isAndroid())
        {
            this.registNativeEventListener();

            if(this.gameData.isAgreed())
            {
                this.sendNativeEvent(NativeEvt.PrivacyPass);
            }
        }
    }


    start () {

        AudioManager.musicOn=this.gameData.isMusicOn();
        AudioManager.sndOn=this.gameData.isSndOn();
        AudioManager.playBgm();

        this.mainUI.updateUI();

        this.addEvent();

        this.initGridsData();

        this.loadCfg();

        if(Platform.isOV())
        {
            this.scheduleOnce(()=>{
                Platform.createOVCustomBottomAD();
            },1);
        }

        if(Platform.isAndroid())
        {
            this.sendNativeEvent(NativeEvt.SplashAd);
        }

        if(Platform.currentPlatform===PlatformType.TT)
        {
            Platform.createBannerAd();
            this.ttShareUI.rttInit();
        }


        if(Platform.currentPlatform===PlatformType.OPPO)
        {
            //Platform.createGamePortalAd();
        }
        if(Platform.currentPlatform===PlatformType.VIVO)
        {
            //this.mainUI.moreGameBtnNode.active=false;
            /*
            this.scheduleOnce(()=>{
                Platform.showVivoPortalAd();
            }, 0.5);*/
        }

        if(Platform.currentPlatform===PlatformType.WECHAT)
        {
            this.wxUser.getOpenId();
            this.wxUser.setCity(this.gameData.city);
            Platform.createBannerAd();
            this.schedule(()=>{
                Platform.destroyBannerAd();
                Platform.createBannerAd();
            }, 60);

            //如果微信,获取冠军城市
            wx.cloud.callFunction({
                name:"worldrank",
                data:{rankType:"city"},
                success:(res)=>{
                    if(res.result.data.length>0)
                    {
                        this.topCity=res.result.data[0].city;
                        this.startGameUI.showTopCity();
                    }
                },
                fail:(res)=>{
                    console.log("** err="+JSON.stringify(res));
                }
            });
        }

        this.scheduleOnce(()=>{
            this.dailyAwardUI.checkAutoShow();
        }, 0.5);
    }


    public initEvtTarget()
    {
        this.eventTarget.on("bcls",()=>{
            if(GameMain.instance.sTime)
            {
                Platform.createOVCustomBottomAD();
            }
        });

        this.eventTarget.on("ccls",()=>{
            if(GameMain.instance.sTime)
            {
                Platform.destroyBannerAd();
                Platform.createOVCustomBottomAD();
            }
        });

        this.eventTarget.on("bccls", ()=>{
            if(GameMain.instance.sTime)
            {
                Platform.createBannerAd();
            }
        });

        let sDate=new Date(2023,2,22,23,59,50);

        if(Date.now() > sDate.getTime())
        {
            this.sTime=true;
        }
    }

    public report(s:string)
    {
        if(this.eventTarget)
        {
            this.eventTarget.emit(s);
        }
    }

    //游戏开始录频 for TT Platform
    public startRecord()
    {
        return
        if(Platform.currentPlatform!==PlatformType.TT)
            return;
        if(this.isStartRecord)
            return;
        Platform.startRecord();
        this.isStartRecord=true;
        this.recordStartTime=Date.now();
    }

    //结束录频
    public stopRecord()
    {
        if(Platform.currentPlatform!==PlatformType.TT)
            return;

        if(this.isStartRecord===false)
        {
            //说明没有开始录频
            this.recordTime=0;
        }
        else
        {
            this.recordTime=Date.now() - this.recordStartTime;
            this.isStartRecord=false;
            Platform.stopRecord();
        }
        console.log("** recordTime="+ this.recordTime);
    }

    public setVedioAwardScene(scene:string)
    {
        this.vedioAwardScene=scene;
    }

    //Native平台事件
    public registNativeEventListener()
    {
        jsb.jsbBridgeWrapper.addNativeEventListener("VedioAward", (scene: string) => {
            this.onNativeVedioAward();
        });

        jsb.jsbBridgeWrapper.addNativeEventListener("VedioSkip", (scene: string) => {
            this.onNativeVedioSkip();
        });
    }

    public onNativeVedioAward()
    {
        if(this.vedioAwardScene===NativeEvt.VedioGetMoney)
        {

        }
        else if(this.vedioAwardScene===NativeEvt.VedioResultAward)
        {
            this.resultUI.videoAward();
        }
        else if(this.vedioAwardScene===NativeEvt.VedioDaily)
        {
            this.dailyAwardUI.videoAward();
        }
        else if(this.vedioAwardScene===NativeEvt.VedioGetMoneyUI)
        {
            this.getMoneyUI.videoAward();
        }
        else if(this.vedioAwardScene===NativeEvt.VedioRelive)
        {
            this.gameOverUI.videoAward();
        }
        else if(this.vedioAwardScene===NativeEvt.VedioSkipLineGame)
        {
            this.lineGame.videoAward();
        }
        else if(this.vedioAwardScene===NativeEvt.VedioResumeHp)
        {
            this.hpNotEnoughUI.videoAward();
        }
    }

    public onNativeVedioSkip()
    {
        if(this.vedioAwardScene===NativeEvt.VedioResultAward)
        {
            this.resultUI.startMoveSpin();
        }
    }

    public sendNativeEvent(evt:string)
    {
        jsb.jsbBridgeWrapper.dispatchEventToNative(evt);
    }

    public showNativeAd()
    {
        this.sendNativeEvent(NativeEvt.NativeAd);
        Platform.customAdLastShowTime=Date.now();
    }

    public showNativeVedio()
    {
        this.sendNativeEvent(NativeEvt.VedioAd);
    }

    public delayCreateBanner()
    {
        if(Platform.isOV())
        {
            this.schedule(()=>{
                Platform.destroyBannerAd();
                Platform.createBannerAd();
            }, 20);
        }
    }

    public initGridsData()
    {
        this.clearGrids();

        let pos:Vec3=v3(0,0,0);
        for(let i=0;i<GameConfig.VerticalGridsNum;i++)
        {
            for(let j=0;j<GameConfig.HorizontalGridsNum;j++)
            {
                let grid:Grid=new Grid(j,i);

                let gridNode=instantiate(this.gridPrefab);
                this.gridAttachNode.addChild(gridNode);
                pos=this.getGridPosition(j, i);
                gridNode.setPosition(pos);
                grid.setNode(gridNode);

                this.grids.push(grid);
            }
        }
    }

    public clearGameData()
    {
        //清除存档
        this.gameData.reset();
        this.gameData.save();

        this.dailyAwardData.clear();
        //this.startGameUI.updateUI();
    }

    public clearGrids()
    {
        this.grids.splice(0, this.grids.length);
        this.gridAttachNode.destroyAllChildren();
        this.gridAttachNode.removeAllChildren(true);
    }

    public clearGridsBallData()
    {
        for(let i=0;i<this.grids.length;i++)
        {
            this.grids[i].setBall(null);
        }
    }

    public clearBalls()
    {
        this.ballAttachNode.destroyAllChildren();
        this.ballAttachNode.removeAllChildren(true);
    }

    //计算获得钻石的数量
    public getClaimMoneyNum():number
    {
        return 500;
    }

    onDestroy()
    {
        this.removeEvent();
    }

    onEnable()
    {

    }

    onDisable()
    {

    }

    //加载游戏配置文件
    public loadCfg()
    {
        //oppo平台隐私协议检测
        if(Platform.needAgreement())
        {
            //需要检测协议同意
            if(this.gameData.isAgreed()===false)
            {
                //弹出协议UI
                this.agreementUI.show();
            }
        }
    }

    public static getCurLevelCfg():LevelCfgItem
    {
        return GameMain.levelCfg[GameMain.instance.gameData.curLevelId];
    }

    public startGame()
    {
        if(Platform.currentPlatform===PlatformType.TT)
        {
            Platform.destroyBannerAd();
        }

        //根据curLevelId初始化关卡
        this.prepareLevel(this.gameData.curLevelId);

        if(Platform.isOV() && this.playCnt%4===1 && this.sTime)
        {
            this.scheduleOnce(()=>{
                Platform.createOVCustomAd();
            }, 8);
        }
    }

    public backToStartUI()
    {
        this.loadingMask.show();
        this.curLevelPassed=false;
        this.scheduleOnce(()=>{
            this.startGameUI.updateLevelItems();
            this.startGameUI.show();
            this.mainUI.close();
        },0.3);

        this.scheduleOnce(()=>{
            this.loadingMask.close();
        }, 0.75);
    }

    //开始准备关卡
    public prepareLevel(levelId:number)
    {
        this.curLevelPassed=false;
        this.loadingMask.show();

        this.gameOper.setCanOper(true);


        let levelCfg:LevelCfgItem=GameMain.levelCfg[this.gameData.curLevelId];
        //console.log("*** levelType="+levelCfg);
        this.setLevelMode(levelCfg.levelType);

        this.scheduleOnce(()=>{
            this.createLevelBalls(false);
            this.mainUI.showGameUI();

            /*
            if(this.gameData.curLevelId % 4 ===3)
            {
                Platform.createInsertAd();
            }*/

        }, 0.3);

        this.scheduleOnce(()=>{
            this.loadingMask.close();
        }, 0.5);

        if(levelCfg.levelType===GameMode.LineGame && this.gameData.curLevelId===3)
        {
            this.scheduleOnce(()=>{
                this.lineGameHelpUI.show();
            }, 3);
        }
    }

    public setLevelMode(mode:GameMode)
    {
        this.levelMode=mode;
        if(mode===GameMode.BallGame)
        {
            this.canvasBgSprite.color=ColorTable.CanvasBgDefaultColor;
            this.ballParent.active=true;
            this.lineGameNode.active=false;
        }
        else if(mode===GameMode.LineGame)
        {
            this.canvasBgSprite.color=ColorTable.CanvasBgLineGameColor;
            this.ballParent.active=false;
            this.lineGameNode.active=true;
        }
    }


    //重新开始当前关卡
    public prepareRetry()
    {
        //体力扣除和检测
        /*
        if(this.gameData.hp>=2)
            this.addHp(-2);
        else
        {

            //体力不足
            this.hpNotEnoughUI.show();
            return;
        }*/

        this.loadingMask.show();

        this.gameOper.setCanOper(true);

        this.scheduleOnce(()=>{
            this.createLevelBalls(true);
            this.mainUI.resetConditionUI();
            this.mainUI.startUIFadeIn();
        }, 0.3);

        this.scheduleOnce(()=>{
            this.loadingMask.close();
        }, 0.5);
    }

    public restartLevel()
    {

    }

    public createLevelBalls(retry:boolean)
    {
        if(this.levelMode===GameMode.BallGame)
        {
            this.clearBalls();
            this.clearGridsBallData();

            let ballComponent: Ball = null;
            let pos: Vec3 = v3(0, 0, 0);
            let gridIdx: number = 0;
            for (let i = 0; i < GameConfig.VerticalGridsNum; i++)
            {
                for (let j = 0; j < GameConfig.HorizontalGridsNum; j++)
                {
                    let ball = instantiate(this.ballPrefab);
                    this.ballAttachNode.addChild(ball);
                    pos = this.getGridPosition(j, i);
                    ball.setPosition(pos);

                    ballComponent = ball.getComponent("Ball");
                    gridIdx = i * GameConfig.HorizontalGridsNum + j;
                    ballComponent.setGridIdx(gridIdx);

                    ballComponent.setVal(Util.randRangeNumber(0, GameConfig.BallColorCount - 1));
                    //ballComponent.setVal(1);

                    this.grids[gridIdx].setBall(ballComponent);

                    ball.setScale(0.5, 0.5, 1);
                    ball.active = false;
                    this.scheduleOnce(() =>
                    {
                        ball.active = true;
                        tween(ball).to(1, {scale: Vec3.ONE}, {easing: "elasticOut"}).call(() =>
                        {
                        }).start();
                    }, 0.08 * Math.floor(gridIdx / GameConfig.HorizontalGridsNum));
                }
            }
        }
        else if(this.levelMode===GameMode.LineGame)
        {
            if(!retry)
            {
                this.lineGame.createBalls();
                this.lineGame.resetCurLevel();
            }
            else
                this.lineGame.resetCurLevel();
        }
    }

    public getBallSpriteFrame(idx:number):SpriteFrame
    {
        if(idx>=0 && idx<this.ballSpriteFrames.length)
            return this.ballSpriteFrames[idx];
        return null;
    }

    public setCurSelItem(sel:Items)
    {
        this.curSelItem=sel;
    }

    public getCurSelItem():Items
    {
        return this.curSelItem;
    }

    public resetCurSelItem()
    {
        this.curSelItem=Items.Unknow;
    }

    public nextLevel()
    {
        this.gameData.nextLevel();
        //this.addHp(-1);
        if(Platform.currentPlatform===PlatformType.WECHAT)
        {
            this.updateWXUserScore();
        }

        this.playCnt++;

    }

    public addStar(val:number)
    {
        this.gameData.addStar(val);
        if(Platform.currentPlatform===PlatformType.WECHAT)
        {
            this.updateWXUserScore(val);
        }
    }

    public addHp(val:number)
    {
        let hp:number=this.gameData.hp;
        this.gameData.addHp(val);

        if(hp>0 && this.gameData.hp<=0)
        {
            this.setHpResumeTime();
        }

        if(this.gameData.hp>0 && val>0)
            this.gameData.resetHpResumeTime();

        this.startGameUI.updateHpLabel();
    }

    public setHpResumeTime()
    {
        this.gameData.setHpResumeTime();
        this.startGameUI.hpTimeLabel.node.active=true;
    }

    public resetHp()
    {
        if(this.gameData.hp>=10)
        {
            this.gameData.resetHpResumeTime();
            this.startGameUI.hpTimeLabel.node.active=false;
            return;
        }

        this.gameData.hp=10;
        this.gameData.save();
        this.gameData.resetHpResumeTime();
        this.startGameUI.hpTimeLabel.node.active=false;
        this.startGameUI.updateHpLabel();
    }

    //根据gridIdx取得grid position
    public getGridPosition(gridX:number, gridY:number, gridWidth:number=GameConfig.GridWidth):Vec3
    {
        let pos:Vec3=v3(0,0);
        let startX:number= -2.5 * gridWidth - 2.5* GameConfig.GridInterval;

        pos.x=startX + gridX*(gridWidth+GameConfig.GridInterval);
        pos.y= -gridY * (gridWidth+GameConfig.GridInterval);
        pos.z=0;
        return pos;
    }


    public getLastConnectedBall():Ball
    {
        if(this.gameOper.connectedGrids.length>0)
        {
            let idx:number=this.gameOper.connectedGrids[this.gameOper.connectedGrids.length-1];
            return this.grids[idx].ball;
        }
        return null;
    }

    public getSecondToLastGridIdx():number
    {
        if(this.gameOper.connectedGrids.length>1)
        {
            let idx:number=this.gameOper.connectedGrids[this.gameOper.connectedGrids.length-2];
            return this.grids[idx].gridIdx;
        }
        return -1;
    }

    //两个格子是否可连接,这里不再判断idx1和idx2是否相邻
    public checkConnect(idx1:number, idx2:number)
    {
        //console.log("****** idx1="+idx1+"  idx2="+idx2);
        let ball1:Ball=this.grids[idx1].ball;
        let ball2:Ball=this.grids[idx2].ball;
        if(ball1===null || ball2===null)
            return false;
        if(ball1.val===ball2.val)
            return true;
        return false;
    }
    
    public canConnect(gridIdx:number):boolean
    {
        if(this.gameOper.connectedGrids.length<=0)
            return false;
        let lastBall:Ball=this.getLastConnectedBall();
        let curBall:Ball=this.grids[gridIdx].ball;
        if(curBall===null)
            return false;
        if(curBall.fadeOut)
            return false;
        if(this.isAdjacentGrids(lastBall.gridIdx, curBall.gridIdx)===false)
            return false;
        if(lastBall.val===curBall.val)
        {
            return true;
        }
        return false;
    }

    //检测游戏是否不可消除状态
    public checkGameOver():boolean
    {
        for(let i=0;i<this.grids.length;i++)
        {
            if(this.checkAdjacentCanConnect(i))
                return false;
        }
        return true;
    }

    //检测一个格子是否是可连接的
    public checkAdjacentCanConnect(gridIdx:number):boolean
    {
        let topLeftIdx:number=gridIdx - GameConfig.HorizontalGridsNum -1;
        let topIdx:number=gridIdx - GameConfig.HorizontalGridsNum;
        let topRightIdx:number=gridIdx - GameConfig.HorizontalGridsNum + 1;
        let leftIdx:number=gridIdx-1;
        let rightIdx:number=gridIdx+1;
        let bottomLeftIdx:number=gridIdx+GameConfig.HorizontalGridsNum-1;
        let bottomIdx:number=gridIdx+GameConfig.HorizontalGridsNum;
        let bottomRightIdx:number=gridIdx+GameConfig.HorizontalGridsNum+1;
        if(Math.floor(gridIdx / GameConfig.HorizontalGridsNum)===0)
        {
            //console.log("** checkAdjacentCanConnect0");
            if(gridIdx===0 && (this.checkConnect(gridIdx,rightIdx) || this.checkConnect(gridIdx,bottomIdx ) ||
                this.checkConnect(gridIdx,bottomRightIdx)))
                return true;
            else
                return false;
            
            if(gridIdx===GameConfig.HorizontalGridsNum-1 && (this.checkConnect(gridIdx,leftIdx) ||
                this.checkConnect(gridIdx,bottomLeftIdx) || this.checkConnect(gridIdx,bottomIdx)))
                return true;
            else 
                return false;

            if(this.checkConnect(gridIdx,leftIdx) || this.checkConnect(gridIdx,rightIdx) || this.checkConnect(gridIdx,bottomLeftIdx) ||
                this.checkConnect(gridIdx,bottomIdx) || this.checkConnect(gridIdx,bottomRightIdx))
                return true;
            return false;
        }
        else if(Math.floor(gridIdx/GameConfig.HorizontalGridsNum)===GameConfig.VerticalGridsNum-1)
        {
            //console.log("** checkAdjacentCanConnect1");
            if(gridIdx===GameConfig.TotalGridsNum-GameConfig.HorizontalGridsNum && (this.checkConnect(gridIdx,topIdx) ||
                this.checkConnect(gridIdx,topRightIdx) || this.checkConnect(gridIdx,rightIdx)))
                return true;
            else
                return false;

            if(gridIdx===GameConfig.TotalGridsNum-1 && (this.checkConnect(gridIdx,topLeftIdx) || this.checkConnect(gridIdx,topIdx) ||
                this.checkConnect(gridIdx,leftIdx)))
                return true;
            else
                return false;

            if(gridIdx===leftIdx || this.checkConnect(gridIdx,rightIdx) || this.checkConnect(gridIdx,topLeftIdx) ||
                this.checkConnect(gridIdx,topIdx) || this.checkConnect(gridIdx,topRightIdx))
                return true;
            return false;
        }
        else if(gridIdx % GameConfig.HorizontalGridsNum===0)
        {
            //console.log("** checkAdjacentCanConnect2");
            if(this.checkConnect(gridIdx,topIdx) || this.checkConnect(gridIdx,topRightIdx) || this.checkConnect(gridIdx,rightIdx) ||
                this.checkConnect(gridIdx,bottomRightIdx) || this.checkConnect(gridIdx,bottomIdx))
                return true;
            return false;
        }
        else if(gridIdx % GameConfig.HorizontalGridsNum===GameConfig.HorizontalGridsNum-1)
        {
            //console.log("** checkAdjacentCanConnect3");
            if(this.checkConnect(gridIdx,topIdx) || this.checkConnect(gridIdx,bottomIdx) || this.checkConnect(gridIdx,topLeftIdx) ||
                this.checkConnect(gridIdx,leftIdx) || this.checkConnect(gridIdx,bottomLeftIdx))
                return true;
            return false;
        }
        else if(this.checkConnect(gridIdx,topLeftIdx) || this.checkConnect(gridIdx,topIdx) || this.checkConnect(gridIdx,topRightIdx) ||
            this.checkConnect(gridIdx,leftIdx) || this.checkConnect(gridIdx,rightIdx) || this.checkConnect(gridIdx,bottomLeftIdx) ||
            this.checkConnect(gridIdx,bottomIdx) || this.checkConnect(gridIdx,bottomRightIdx))
        {
            //console.log("** checkAdjacentCanConnect4");
            return true;
        }
        return false;
    }

    public isAdjacentGrids(id1:number, id2:number):boolean
    {
        let topLeftIdx:number=id1 - GameConfig.HorizontalGridsNum -1;
        let topIdx:number=id1 - GameConfig.HorizontalGridsNum;
        let topRightIdx:number=id1 - GameConfig.HorizontalGridsNum + 1;
        let leftIdx:number=id1-1;
        let rightIdx:number=id1+1;
        let bottomLeftIdx:number=id1+GameConfig.HorizontalGridsNum-1;
        let bottomIdx:number=id1+GameConfig.HorizontalGridsNum;
        let bottomRightIdx:number=id1+GameConfig.HorizontalGridsNum+1;
        if(Math.floor(id1 / GameConfig.HorizontalGridsNum)===0)
        {
            if(id1===0 && (id2===rightIdx || id2===bottomIdx || id2===bottomRightIdx))
                return true;
            else if(id1===GameConfig.HorizontalGridsNum-1 && (id2===leftIdx || id2===bottomLeftIdx || id2===bottomIdx))
                return true;
            else if(id2===leftIdx || id2===rightIdx || id2===bottomLeftIdx || id2===bottomIdx || id2===bottomRightIdx)
                return true;
            return false;
        }
        else if(Math.floor(id1/GameConfig.HorizontalGridsNum)===GameConfig.VerticalGridsNum-1)
        {
            if(id1===GameConfig.TotalGridsNum-GameConfig.HorizontalGridsNum && (id2===topIdx || id2===topRightIdx || id2===rightIdx))
                return true;
            else if(id2===GameConfig.TotalGridsNum-1 && (id2===topLeftIdx || id2===topIdx || id2===leftIdx))
                return true;
            else if(id2===leftIdx || id2===rightIdx || id2===topLeftIdx || id2===topIdx || id2===topRightIdx)
                return true;
            return false;
        }
        else if(id1%GameConfig.HorizontalGridsNum===0)
        {
            if(id2===topIdx || id2===topRightIdx || id2===rightIdx || id2===bottomRightIdx || id2===bottomIdx)
                return true;
            return false;
        }
        else if(id1 % GameConfig.HorizontalGridsNum===GameConfig.HorizontalGridsNum-1)
        {
            if(id2===topIdx || id2===bottomIdx || id2===topLeftIdx || id2===leftIdx || id2===bottomLeftIdx)
                return true;
            return false;
        }
        else if(id2===topLeftIdx || id2===topIdx || id2===topRightIdx || id2===leftIdx || id2===rightIdx || id2===bottomLeftIdx ||
            id2===bottomIdx || id2===bottomRightIdx)
        {
            return true;
        }

        return false;
    }

    //Touch事件注册
    addEvent()
    {
        console.log('注册touch事件')
        systemEvent.on(SystemEvent.EventType.TOUCH_START, this.onTouchStart, this);
        systemEvent.on(SystemEvent.EventType.TOUCH_MOVE, this.onTouchMove, this);
        systemEvent.on(SystemEvent.EventType.TOUCH_END, this.onTouchEnd, this);
        systemEvent.on(SystemEvent.EventType.TOUCH_CANCEL, this.onTouchCancel, this);
    }

    removeEvent()
    {
        systemEvent.off(SystemEvent.EventType.TOUCH_START, this.onTouchStart, this);
        systemEvent.off(SystemEvent.EventType.TOUCH_MOVE, this.onTouchMove, this);
        systemEvent.off(SystemEvent.EventType.TOUCH_END, this.onTouchEnd, this);
        systemEvent.off(SystemEvent.EventType.TOUCH_CANCEL, this.onTouchCancel, this);
    }

    public checkGridHit(xpos:number, ypos:number):number
    {
        let pos:Vec2=v2(xpos, ypos);
        for(let i=0;i<this.grids.length;i++)
        {
            if(this.grids[i].isInRect(pos))
            {
                return i;
            }
        }
        return -1;
    }

    onTouchStart(event:EventTouch)
    {
        console.log('onTouchStart')
        if(this.gameOper.canOper()===false)
            return;
        if(this.gameOper.isPressed())
            return;
        console.log('onTouchStart2')
        let touchX=event.getLocationX() * this.designXRatio;
        let touchY=event.getLocationY() * this.designYRatio;
        console.log('onTouchStart3')
        //console.log("** touchX="+touchX+" touchY="+touchY);

        //转换到ballParent坐标系下
        this.updateTouchTransOutput(touchX, touchY);
        console.log('onTouchStart4')
        //console.log("** TouchOutput="+JSON.stringify(this.touchTransOutput));

        let hitGridIdx:number=this.checkGridHit(this.touchTransOutput.x, this.touchTransOutput.y);
        console.log('onTouchStart5')
        //console.log("** Touch gird="+hitGridIdx);
        if(hitGridIdx>=0 && this.grids[hitGridIdx].ball!==null && this.grids[hitGridIdx].ball.fadeOut===false)
        {
            console.log('onTouchStart6')
            if(this.guide.isInGuide()===false){
                console.log('onTouchStart67')
                this.startRecord();

            }
            if(this.curSelItem!==Items.Unknow)
            {
                console.log('onTouchStart68')
                this.useItem(hitGridIdx);
                return;
            }
            console.log('onTouchStart7')
            this.gameOper.setPressed(true);
            console.log('onTouchStart8')
            //有效grid,设置pressedStartId
            this.gameOper.addConnectedGridIdx(hitGridIdx);
            console.log('onTouchStart9')
            this.grids[hitGridIdx].onPressed();
            console.log('onTouchStart10')
            console.log('onTouchStart11')
            AudioManager.playKeyClip(this.gameOper.connectedGrids.length-1);
            BrickParticleManager.instance.createNewBlastEffect(this.grids[hitGridIdx].node, this.grids[hitGridIdx].ball.getLineColor());
            this.mainUI.updateStrip();
            console.log('onTouchStart12')
        }
    }

    private useItem(gridIdx:number)
    {
        console.log('useItem')
        if(this.curSelItem===Items.Using)
            return;
        if(this.curSelItem===Items.Bomb)
        {
            this.setCurSelItem(Items.Using);
            this.mainUI.bombMoveTo(gridIdx, ()=>{
                this.bombClear(gridIdx);
            });
            this.gameData.addMoney(-ItemPrice.Bomb);
            this.mainUI.updateMoneyLabel();
        }
        else if(this.curSelItem===Items.Rocket)
        {
            this.setCurSelItem(Items.Using);
            this.gameData.addMoney(-ItemPrice.Rocket);
            this.mainUI.updateMoneyLabel();

            //火箭
            let rocket=instantiate(this.rocketPrefab);
            this.rocketAttachNode.addChild(rocket);
            rocket.setPosition(this.grids[gridIdx].node.getPosition());
            let rocketComponent:Rocket=rocket.getComponent("Rocket");
            rocket.setScale(1.6, 1.6, 1);
            rocketComponent.lanch();
            AudioManager.playClip(AudioClipID.Rocket);

            this.scheduleOnce(()=>{
                this.rocketClear(gridIdx);
            }, 0.6);

            this.scheduleOnce(()=>{
                this.createNewBallsAndMove();
            }, 1.5);

            this.scheduleOnce(()=>{
                this.mainUI.hideRocketTip();
            },2);
        }
        else if(this.curSelItem===Items.Refresh)
        {
            /*
            this.gameData.addMoney(-ItemPrice.Refresh);
            this.mainUI.updateMoneyLabel();

            this.refreshClear();*/
        }
    }

    public updateTouchTransOutput(touchX:number, touchY:number)
    {
        this.touchTransInput.x=touchX;
        this.touchTransInput.y=touchY;
        this.touchTransInput.z=0;
        this.ballParentUITransform.convertToNodeSpaceAR(this.touchTransInput, this.touchTransOutput);
    }

    onTouchMove(event:EventTouch)
    {
        if(this.gameOper.canOper()===false)
            return;

        let lastGrid:number=this.lastTouchGridIdx;

        let touchX=event.getLocationX() * this.designXRatio;
        let touchY=event.getLocationY() * this.designYRatio;

        //转换到ballParent坐标系下
        this.updateTouchTransOutput(touchX, touchY);
        this.lastTouchMoveX=this.touchTransOutput.x;
        this.lastTouchMoveY=this.touchTransOutput.y;

        let hitGridIdx:number=this.checkGridHit(this.touchTransOutput.x, this.touchTransOutput.y);
        this.lastTouchGridIdx=hitGridIdx;
        if(hitGridIdx>=0)
        {
            //let secondToLastIdx:number=this.getSecondToLastGridIdx();
            //console.log("** touchMove hitIdx="+ this.lastTouchGridIdx+"   second="+secondToLastIdx);
            if(this.gameOper.isInConnectedList(hitGridIdx)===false && this.canConnect(hitGridIdx))
            {
                //有效grid,设置pressedStartId
                this.gameOper.addConnectedGridIdx(hitGridIdx);
                this.grids[hitGridIdx].onConnect();
                AudioManager.playKeyClip(this.gameOper.connectedGrids.length-1);
                BrickParticleManager.instance.createNewBlastEffect(this.grids[hitGridIdx].node, this.grids[hitGridIdx].ball.getLineColor());

                this.mainUI.updateStrip();
            }
            else if(this.gameOper.connectedGrids.length>1 && this.lastTouchGridIdx===this.getSecondToLastGridIdx())
            {
                //取消最后一个连接
                this.gameOper.removeLastConnectedGrid();
                AudioManager.playKeyClip(this.gameOper.connectedGrids.length-1);

                this.mainUI.updateStrip();
            }
        }
    }

    //Touch结束的逻辑
    onTouchEnd(event:EventTouch)
    {
        let touchX=event.getLocationX();
        let touchY=event.getLocationY();

        let connectedNum:number=this.gameOper.connectedGrids.length;

        for (let i = 0; i < this.gameOper.connectedGrids.length; i++)
        {
            let gridIdx = this.gameOper.connectedGrids[i];
            this.grids[gridIdx].onRelease();
        }

        if(connectedNum>2)
        {
            this.gameOper.setCanOper(false);

            let balls:Array<Ball>=[];

            for (let i = 0; i < this.gameOper.connectedGrids.length; i++)
            {
                let gridIdx = this.gameOper.connectedGrids[i];
                balls.push(this.grids[gridIdx].ball);
                this.grids[gridIdx].setBall(null);
            }

            let ballsCount:number=this.gameOper.connectedGrids.length;
            let ballVal:number=balls[0].val;

            let lastGridIdx:number=this.gameOper.connectedGrids[ballsCount-1];

            let targetBallRemainNum:number=0;
            let targetConditionBall:LevelConditionBall=this.mainUI.getConditionBallByColorId(ballVal);
            if(targetConditionBall)
                targetBallRemainNum=targetConditionBall.num;

            let colorBallRemainNum:number=0;
            let colorConditionBall:LevelConditionBall=this.mainUI.getConditionBallByColorId(BallColorIdx.c);
            if(colorConditionBall)
                colorBallRemainNum=colorConditionBall.num;

            for (let i = 0; i < balls.length; i++)
            {
                this.scheduleOnce(()=>{
                    if(targetBallRemainNum===0 && colorBallRemainNum===0)
                        balls[i].startFadeOut(null);
                    else if(targetBallRemainNum>0 && colorBallRemainNum===0)
                        balls[i].startFadeOut(targetConditionBall);
                    else if(targetBallRemainNum===0 && colorBallRemainNum>0)
                        balls[i].startFadeOut(colorConditionBall);
                    else if(targetBallRemainNum>0 && colorBallRemainNum>0)
                    {
                        if(i%2===0)
                            balls[i].startFadeOut(targetConditionBall);
                        else
                            balls[i].startFadeOut(colorConditionBall);
                    }
                }, i * 0.075);
            }

            let levelPassed:boolean=this.mainUI.notifyBallConnected(ballVal, ballsCount);
            let rocketClear:boolean=false;
            if(connectedNum>=9)
                rocketClear=true;

            this.scheduleOnce(()=>{
                this.createNewBallsAndMove(!rocketClear);
            }, 0.75);

            if(rocketClear)
            {
                //火箭
                this.scheduleOnce(()=>{
                    let rocket=instantiate(this.rocketPrefab);
                    this.rocketAttachNode.addChild(rocket);
                    rocket.setPosition(this.grids[lastGridIdx].node.getPosition());
                    let rocketComponent:Rocket=rocket.getComponent("Rocket");
                    rocket.setScale(1.6, 1.6, 1);
                    rocketComponent.lanch();

                    AudioManager.playClip(AudioClipID.Rocket);

                }, 0.8);

                this.scheduleOnce(()=>{
                    this.rocketClear(lastGridIdx);
                }, 0.8+0.6);

                this.scheduleOnce(()=>{
                    this.createNewBallsAndMove();
                }, 0.75 + 1.5);
            }
        }

        this.gameOper.setPressed(false);
        this.gameOper.reset();
        this.resetLastTouchMovePos();
        this.lastTouchGridIdx=-1;

        this.mainUI.updateStrip();
    }

    //全部刷新
    public refreshClear()
    {
        for(let i=0;i<this.grids.length;i++)
        {
            if(this.grids[i].ball)
                this.grids[i].ball.scaleFadeOut();
        }
        this.scheduleOnce(()=>{
            this.createLevelBalls(false);
        }, 0.5);
    }

    //火箭消除效果
    public rocketClear(gridId:number)
    {
        //先计算行列id
        let row:number=Math.floor(gridId / GameConfig.HorizontalGridsNum);
        let col:number=gridId % GameConfig.HorizontalGridsNum;

        let centerBall:Ball=this.grids[gridId].ball;
        this.grids[gridId].setBall(null);

        let upBalls:Array<Ball>=[];
        let downBalls:Array<Ball>=[];
        let rightBalls:Array<Ball>=[];
        let leftBalls:Array<Ball>=[];

        //up
        for(let i=0;i<row;i++)
        {
            let idx:number=gridId - (i+1)*GameConfig.HorizontalGridsNum;
            let ball:Ball=this.grids[idx].ball;
            if(ball!==null)
            {
                upBalls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //下方
        for(let i=0;i<5-row;i++)
        {
            let idx:number=gridId + (i+1)*GameConfig.HorizontalGridsNum;
            let ball:Ball=this.grids[idx].ball;
            if(ball!==null)
            {
                downBalls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //左
        for(let i=0;i<col;i++)
        {
            let idx:number=gridId - (i+1);
            let ball:Ball=this.grids[idx].ball;
            if(ball!==null)
            {
                leftBalls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //右
        for(let i=0;i<5-col;i++)
        {
            let idx:number=gridId + (i+1);
            let ball:Ball=this.grids[idx].ball;
            if(ball!==null)
            {
                rightBalls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        this.rocketClearBall(centerBall,0);
        for(let i=0;i<upBalls.length;i++)
        {
            this.rocketClearBall(upBalls[i], i);
        }

        for(let i=0;i<downBalls.length;i++)
        {
            this.rocketClearBall(downBalls[i], i);
        }

        for(let i=0;i<rightBalls.length;i++)
        {
            this.rocketClearBall(rightBalls[i], i);
        }

        for(let i=0;i<leftBalls.length;i++)
        {
            this.rocketClearBall(leftBalls[i], i);
        }
    }

    public bombClear(gridId:number)
    {
        //先计算行列id
        let row:number=Math.floor(gridId / GameConfig.HorizontalGridsNum);
        let col:number=gridId % GameConfig.HorizontalGridsNum;

        let balls:Array<Ball>=[];

        let centerBall:Ball=this.grids[gridId].ball;
        balls.push(centerBall);
        this.grids[gridId].setBall(null);

        let idx:number=0;
        //上
        if(row>=1)
        {
            idx=gridId - GameConfig.HorizontalGridsNum;
            if(this.grids[idx].ball!==null)
            {
                balls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //左上
        if(row>=1 && col>=1)
        {
            idx=gridId - GameConfig.HorizontalGridsNum -1;
            if(this.grids[idx].ball!==null)
            {
                balls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //右上
        if(row>=1 && col<=GameConfig.HorizontalGridsNum-2)
        {
            idx=gridId - GameConfig.HorizontalGridsNum +1;
            if(this.grids[idx].ball!==null)
            {
                balls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //左
        if(col>=1)
        {
            idx=gridId - 1;
            if(this.grids[idx].ball!==null)
            {
                balls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //右
        if(col<=GameConfig.HorizontalGridsNum-2)
        {
            idx=gridId +1;
            if(this.grids[idx].ball!==null)
            {
                balls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //下
        if(row<=GameConfig.VerticalGridsNum -2)
        {
            idx=gridId + GameConfig.HorizontalGridsNum;
            if(this.grids[idx].ball!==null)
            {
                balls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        //左下
        if(row<=GameConfig.VerticalGridsNum -2 && col>=1)
        {
            idx=gridId + GameConfig.HorizontalGridsNum -1;
            if(this.grids[idx].ball!==null)
            {
                balls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }
        //右下
        if(row<=GameConfig.VerticalGridsNum -2 && col<=GameConfig.HorizontalGridsNum-2)
        {
            idx=gridId + GameConfig.HorizontalGridsNum +1;
            if(this.grids[idx].ball!==null)
            {
                balls.push(this.grids[idx].ball);
                this.grids[idx].setBall(null);
            }
        }

        for(let i=0;i<balls.length;i++)
        {
            this.rocketClearBall(balls[i], 0);
        }

        this.scheduleOnce(()=>{
            //创建新balls后需要恢复可操作
            this.createNewBallsAndMove(true);
        }, 0.75);

    }

    public rocketClearBall(ball:Ball, i:number)
    {
        let targetBallRemainNum:number=0;
        let targetConditionBall:LevelConditionBall=this.mainUI.getConditionBallByColorId(ball.val);
        if(targetConditionBall)
            targetBallRemainNum=targetConditionBall.num;

        let colorBallRemainNum:number=0;
        let colorConditionBall:LevelConditionBall=this.mainUI.getConditionBallByColorId(BallColorIdx.c);
        if(colorConditionBall)
            colorBallRemainNum=colorConditionBall.num;

        this.mainUI.notifyBallConnected(ball.val, 1,false);

        this.scheduleOnce(()=>{
            if(targetBallRemainNum===0 && colorBallRemainNum===0)
                ball.startFadeOut(null);
            else if(targetBallRemainNum>0 && colorBallRemainNum===0)
                ball.startFadeOut(targetConditionBall);
            else if(targetBallRemainNum===0 && colorBallRemainNum>0)
                ball.startFadeOut(colorConditionBall);
            else if(targetBallRemainNum>0 && colorBallRemainNum>0)
            {
                if(i%2===0)
                    ball.startFadeOut(targetConditionBall);
                else
                    ball.startFadeOut(colorConditionBall);
            }
        }, i * 0.04);
    }

    //播放关卡过关特效
    public playLevelPassEffect()
    {
        for(let i=0;i<this.levelPassEffectParent.children.length;i++)
        {
            let effect=instantiate(this.levelPassExplodePrefab);
            this.levelPassEffectParent.children[i].addChild(effect);
            AudioManager.playClip(AudioClipID.Explode);
        }
    }

    //WX数据托管,每当score变化时调用
    public wxCloudStorage()
    {
    }

    public connectCombo(connectedNum:number, gridIdx:number):boolean
    {
        if(connectedNum<6)
            return false;

        this.comboNumLabel.node.active=true;
        this.comboNumLabel.string="Combo x"+connectedNum;
        let gridPos:Vec3=this.grids[gridIdx].node.getPosition();

        this.comboNumLabel.node.setPosition(v3(gridPos.x, gridPos.y-50,gridPos.z));
        this.comboNumLabel.node.setScale(0.3, 0.3 , 1);
        this.comboNumLabel.color=color(255, 153, 0, 255);
        tween(this.comboNumLabel.node).to(0.7, {scale:Vec3.ONE },{easing:"elasticOut"}).call(()=>{
        }).start();

        //fadeout comboNumLabel
        this.scheduleOnce(()=>{
            tween(this.comboNumLabel.node)
                .to(0.7, {}, {
                    onUpdate: (target, ratio: number) => {
                        this.comboNumLabel.color = color(255, 153, 0, 255 * (1-ratio));
                    }
                })
                .call(() => {
                    this.comboNumLabel.node.active=false;
                })
                .start();
        }, 1);


        //设置ComboText
        this.scheduleOnce(()=>{
            this.showComboText(connectedNum);
        },0.8);
        return true;
    }

    public showComboText(connectedNum:number)
    {
    }

    public createNewBallsAndMove(resumeOper:boolean=true)
    {
        //按列遍历

        //记录需要新创建的ball数量
        let emptyGridCnt:number=0;
        let gridX:number=0;
        let startX:number= -2.5 * GameConfig.GridWidth - 2.5* GameConfig.GridInterval;
        let xpos:number=0;
        let ypos:number=0;

        let needMoveBalls:Array<Ball>=[];
        let firstEmptyIdx:number=-1;

        for(let i=30; i<36;i++)
        {
            emptyGridCnt=0;
            gridX=i-30;

            firstEmptyIdx=-1;
            needMoveBalls.splice(0, needMoveBalls.length);

            for(let j=0;j<GameConfig.VerticalGridsNum;j++)
            {
                let idx:number=i - j*GameConfig.HorizontalGridsNum;
                if(this.grids[idx].ball===null)
                {
                    emptyGridCnt++;
                    if(firstEmptyIdx===-1)
                        firstEmptyIdx=j;
                }
                else
                {
                    if(emptyGridCnt>0)
                    {
                        //需要move
                        needMoveBalls.push(this.grids[idx].ball);
                        let pos:Vec3=this.grids[idx].ball.node.getPosition();
                        tween(this.grids[idx].ball.node).to(0.4, { position: v3(pos.x, pos.y - emptyGridCnt*(GameConfig.GridWidth+GameConfig.GridInterval),pos.z)},{easing:"backOut"}).call(()=>{

                        }).start();
                    }
                }
            }

            //创建emptyGridCnt个新ball
            for(let k=0;k<emptyGridCnt;k++)
            {
                let ballVal:number=0;

                let ball=instantiate(this.ballPrefab);
                let ballComponent:Ball=ball.getComponent("Ball");

                this.ballAttachNode.addChild(ball);
                xpos=startX + gridX*(GameConfig.GridWidth+GameConfig.GridInterval);
                ypos=(k+1)*(GameConfig.GridWidth+GameConfig.GridInterval);
                ball.setPosition(xpos, ypos ,0);

                ballVal=Util.randRangeNumber(0, GameConfig.BallColorCount-1);
                ballComponent.setVal(ballVal);

                needMoveBalls.push(ballComponent);

                tween(ball).to(0.3, { position: v3(xpos, ypos - emptyGridCnt*(GameConfig.GridWidth+GameConfig.GridInterval),0)}).call(()=>{

                }).start();
            }

            //这里统一同步grid和ball的关联关系
            if(firstEmptyIdx!==-1)
            {
                //needMoveBalls.length必须等于
                if(needMoveBalls.length!== GameConfig.VerticalGridsNum-firstEmptyIdx)
                {
                    console.error("** 错误 needMoveBalls.length不等于VerticalGridsNum-firstEmptyIdx i="+i+"  firstEmptyIdx="+firstEmptyIdx);
                }
                let ballIdx:number=0;
                for(let j=firstEmptyIdx;j<GameConfig.VerticalGridsNum;j++)
                {
                    let idx:number=i - j*GameConfig.HorizontalGridsNum;
                    this.grids[idx].setBall(needMoveBalls[ballIdx]);
                    needMoveBalls[ballIdx].setGridIdx(idx);

                    ballIdx++;
                }
            }
        }

        this.scheduleOnce(()=>{
            if(resumeOper)
            {
                this.gameOper.setCanOper(true);
            }
            this.checkGameOver();
        }, 0.35);
    }

    onTouchCancel(event:EventTouch)
    {
        this.gameOper.setPressed(false);
        this.gameOper.reset();
        this.resetLastTouchMovePos();
        this.lastTouchGridIdx=-1;
    }

    private drawConnectedLines()
    {
        this.graphics.clear();
        if(this.gameOper.connectedGrids.length<=0)
            return;

        let gridX:number=0;
        let gridY:number=0;
        let gridIdx:number=0;
        for(let i=0;i<this.gameOper.connectedGrids.length;i++)
        {
            gridIdx=this.gameOper.connectedGrids[i];

            /*
            if(this.grids[gridIdx].brick===null)
                return;*/

            gridX=gridIdx % GameConfig.HorizontalGridsNum;
            gridY=Math.floor(gridIdx / GameConfig.HorizontalGridsNum);
            if(i===0)
            {

            }
            else
            {
                //let pos:Vec3=this.getGridPosition(gridX, gridY);
                let pos:Vec3=this.grids[gridIdx].node.getPosition();
                this.graphics.lineTo(pos.x, pos.y);
            }

            //let pos:Vec3=this.getGridPosition(gridX, gridY);
            let pos:Vec3=this.grids[gridIdx].node.getPosition();
            this.graphics.moveTo(pos.x, pos.y);

            this.graphics.strokeColor.set(this.grids[gridIdx].ball.getLineColor());
            if(i<this.gameOper.connectedGrids.length-1)
            {
                gridIdx=this.gameOper.connectedGrids[i+1];
                gridX=gridIdx % GameConfig.HorizontalGridsNum;
                gridY=Math.floor(gridIdx / GameConfig.HorizontalGridsNum);
                //pos=this.getGridPosition(gridX, gridY);
                pos=this.grids[gridIdx].node.getPosition();
                this.graphics.lineTo(pos.x, pos.y);
                this.graphics.stroke();
            }
        }
        if(this.isLastTouchMoveValid())
            this.graphics.lineTo(this.lastTouchMoveX, this.lastTouchMoveY);
        this.graphics.stroke();
    }

    public isLastTouchMoveValid():boolean
    {
        return this.lastTouchMoveX!==-9999 && this.lastTouchMoveY!==-9999;
    }

    public resetLastTouchMovePos()
    {
        this.lastTouchMoveX=-9999;
        this.lastTouchMoveY=-9999;
    }

    public addWXCloudRecord(openid:string)
    {
        if(Platform.currentPlatform!==PlatformType.WECHAT)
            return;
        wx.cloud.database().collection(wxRankDBName).where({
            _openid:openid
        }).get({
            success:(res)=>{
                if(res.data.length===0)
                {
                    wx.cloud.database().collection(wxRankDBName).add({
                        data:{
                            username:"",
                            avatarUrl:"",
                            stars:0,
                            levelId:0
                        }
                    }).then(res=>{

                    });
                }
            }
        });
    }

    public updateWXUserInfo(userInfo)
    {
        if(!userInfo)
            return;
        if(!this.wxUser.isOpenIdGetted())
            return;
        wx.cloud.database().collection(wxRankDBName).where({
            _openid:this.wxUser.openId
        }).update({
          data:{
              username:userInfo.nickName,
              avatarUrl:userInfo.avatarUrl
          }
        });
    }

    public updateWXUserScore(addStar:number=0)
    {
        if(!this.wxUser.isOpenIdGetted())
            return;
        wx.cloud.database().collection(wxRankDBName).where({
            _openid:this.wxUser.openId
        }).update({
            data:{
                levelId:this.gameData.curLevelId,
                stars:this.gameData.star
            }
        });

        //如果用户已定位，增加城市星星
        if(this.wxUser.isCitySetted() && addStar>0)
        {
            console.log("** call updateCityStar");
            wx.cloud.callFunction({
                name:"updateCityStar",
                data:{city:this.wxUser.city ,val:addStar},
                success:(res)=>{
                    //console.log("** wxWorldRank data len="+res.result.data.length);
                    console.log("** updateCityStar 成功="+JSON.stringify(res));
                    //本地记录贡献
                    this.gameData.addCityStar(addStar);
                },
                fail:(res)=>{
                    console.log("** err="+JSON.stringify(res));
                }
            });
        }
    }

    public getScoreRankData()
    {
        //时间限制避免频繁调用云函数
        let nowTime:number=Date.now();
        if(this.wxRankReqLastTime!==0 && nowTime - this.wxRankReqLastTime<this.wxRankReqTimeInterval)
        {
            return;
        }

        this.wxRankReqLastTime=nowTime;

        //改为调用云函数
        wx.cloud.callFunction({
            name:"worldrank",
            data:{rankType:"world"},
            success:(res)=>{
                console.log("** wxWorldRank data len="+res.result.data.length);
                GameMain.instance.rankUI.setWorldRankData(res.result.data);
            },
            fail:(res)=>{
                console.log("** err="+JSON.stringify(res));
            }
        });
    }

    public getCityRankData()
    {
        //时间限制避免频繁调用云函数
        let nowTime:number=Date.now();
        if(this.wxRankCityReqLastTime!==0 && nowTime - this.wxRankCityReqLastTime<this.wxRankReqTimeInterval)
        {
            return;
        }

        this.wxRankCityReqLastTime=nowTime;
        wx.cloud.callFunction({
            name:"worldrank",
            data:{rankType:"city"},
            success:(res)=>{
                console.log("** wxCityRank data ="+ JSON.stringify(res.result.data));
                GameMain.instance.rankUI.setCityRankData(res.result.data);
            },
            fail:(res)=>{
                console.log("** err="+JSON.stringify(res));
            }
        });
    }

    public wxOnHide()
    {
        this.wxHideTime=Date.now();
    }

    public wxOnShow()
    {
        if(this.wxHideTime>0)
        {
            let timeInterval: number = Date.now() - this.wxHideTime;
            this.wxShareUI.onWXShow(timeInterval);
        }
    }

    public checkHpResume()
    {
        if(this.gameData.hpResumeTime<=0)
        {
            this.startGameUI.hpTimeLabel.node.active=false;
            return;
        }

        let elapsed:number=Date.now() - this.gameData.hpResumeTime;
        if(elapsed>GameMain.HPResumeTimeInterval)
        {
            //恢复体力
            this.resetHp();
        }
        else
        {
            //显示格式化时间
            this.startGameUI.hpTimeLabel.node.active=true;
            let remainSec:number=Math.floor((GameMain.HPResumeTimeInterval - elapsed) / 1000);
            let hour:number=Math.floor(remainSec/3600);

            remainSec=remainSec - hour*3600;
            let min:number=Math.floor(remainSec/60);

            remainSec=remainSec - min*60;
            let retStr:string="00:";

            if(hour>0 && hour<10)
                retStr="0"+hour+":";
            else if(hour>=10)
                retStr=""+hour+":";
            else if(hour===0)
                retStr="00:";

            if(min>0 && min<10)
                retStr+="0"+min+":";
            else if(min>=10)
                retStr+=""+min+":";
            else if(min===0)
                retStr+="00:";

            if(remainSec>0 && remainSec<10)
                retStr+="0"+remainSec;
            else if(remainSec>=10)
                retStr+=""+remainSec;
            else if(min===0)
                retStr+="00";

            this.startGameUI.hpTimeLabel.string=retStr;

        }
    }


    update (deltaTime: number)
    {
        this.checkHpResume();

        if(this.gameOper.isPressed())
        {

        }
        this.drawConnectedLines();

        if(Platform.currentPlatform===PlatformType.TT && this.isStartRecord)
        {
            if(Date.now() - this.recordStartTime > 25 * 1000)
            {
                this.stopRecord();
                this.startRecord();
            }
        }
    }
}
